// Author: Domien Nowicki, Bjorn Schobben

#include "RapidRaycast.h"
#include <stdio.h>
#include "RaycasterWidget.h"
#include "MapWidget.h"
#include "WorldEditorWidget.h"
#include <QListView>
#include "TextureManagerView.h"
#include "DefaultTextureWidget.h"

#include <QVBoxLayout>
#include <QPushButton>
#include <QFileDialog>
#include <QMessageBox>
#include <QApplication>

RapidRaycast::RapidRaycast(QWidget *parent, Qt::WFlags flags): QMainWindow(parent, flags), raycast(0), world(0), resizeDialog(0), help(0)
{
	ui.setupUi(this);

	// Initialize the help browser
	InitializeHelp();

	vboxLayout = new QVBoxLayout();
	centralWidget()->setLayout(vboxLayout);

	// Create new world editor
	world = new WorldEditorWidget(this);

	QObject::connect(world, SIGNAL(changed()), this, SLOT(slotChanged()));

	// Create new resize dialog
	resizeDialog = new ResizeDialog(this);
	QObject::connect(resizeDialog, SIGNAL(resize(unsigned int, unsigned int)), this, SLOT(slotResize(unsigned int, unsigned int)));


	vboxLayout->addWidget(world);
}


RapidRaycast::~RapidRaycast()
{

}

void RapidRaycast::closeEvent(QCloseEvent* e)
{
	if (world && world->HasChanged())
	{
		int reply = QMessageBox::warning(this, "Warning", "Map is not saved, are you sure you want to quit?", QMessageBox::Ok | QMessageBox::Cancel);

		switch(reply)
		{
			case QMessageBox::Cancel:
				e->ignore();
				return;
		}
	}

	// Also close help window
	if (help)
	{
		 help->close();
	}

	e->accept();
}


void RapidRaycast::on_actionToolPreview_activated()
{
	// Toolbar item clicked, do same as preview menu action
	on_actionPreview_activated();
}

void RapidRaycast::on_actionPreview_activated()
{
	// If not previewing
	if (0 == raycast)
	{
		// Make sure camera is placed
		if (!world->HasPlacedCamera())
		{
			QMessageBox::critical(this, "Error", "Please place a camera first before previewing.");

			return;
		}

		// Choose renderer type
		RaycasterWidget::Renderer rendererType;
		
		if (ui.actionSoftwareRenderer->isChecked())
		{
			rendererType = RaycasterWidget::SOFTWARE;
		}
		else if (ui.actionOpenGLRenderer->isChecked())
		{
			rendererType = RaycasterWidget::OPENGL;
		}

		// Create raycaster
		raycast = new RaycasterWidget(rendererType, this);
		raycast->SetWorld(*world);

		// Hide the editor, and show the raycaster widget
		world->hide();
		vboxLayout->addWidget(raycast);
		vboxLayout->removeWidget(world);
	}
	else
	{
		// Remove the raycaster
		raycast->hide();
		vboxLayout->removeWidget(raycast);
		
		// And show the editor again
		vboxLayout->addWidget(world);

		world->show();

		delete raycast;
		raycast = 0;
	}

	// Update button states
	UpdateStates();
}

void RapidRaycast::on_actionNew_activated()
{
	if (world->HasChanged())
	{
		int reply = QMessageBox::warning(this, "Warning", "Current map is not saved, are you sure you want to start a new map?", QMessageBox::Ok | QMessageBox::Cancel);

		switch(reply)
		{
			case QMessageBox::Cancel:
				return;
		}
	}


	world->Reset();
}

void RapidRaycast::on_actionLoad_activated()
{
	if (world->HasChanged())
	{
		int reply = QMessageBox::warning(this, "Warning", "Current map is not saved, are you sure you want to load a new map?", QMessageBox::Ok | QMessageBox::Cancel);

		switch(reply)
		{
			case QMessageBox::Cancel:
				return;
		}
	}

	// Ask which file to load
	QString qFilename = QFileDialog::getOpenFileName(this, "Open Map File", "", "All map files (*.rmp)");

	// Valid filename?
	if (qFilename.length())
	{
		bool loaded = true;

		try
		{
			world->LoadFromFile(qFilename.toStdString());
		}
		catch(std::runtime_error e)
		{
			loaded = false;
			QMessageBox::critical(this, tr("Error"), e.what());	
		}
		catch(...)
		{
			loaded = false;
			QMessageBox::critical(this, tr("Error"), "Sorry, an error occurred while loading the map.");	
		}


		
		if (loaded)
		{
			mapFilename = qFilename;
			QMessageBox::information(this, "Loading map", "Map was loaded successfully.");	
		}
	}
}

void RapidRaycast::SaveWorld(const QString& qFilename)
{
	bool saved = true;

	try
	{
		world->SaveToFile(qFilename.toStdString());
	}
	catch(std::runtime_error e)
	{
		saved = false;
		QMessageBox::critical(this, tr("Error"), e.what());	
	}
	catch(...)
	{
		saved = false;
		QMessageBox::critical(this, tr("Error"), "Sorry, an error occurred while saving the map.");	
	}

	if (saved)
	{
		mapFilename = qFilename;
		QMessageBox::information(this, "Save", "Map was successfully saved");
	}
}

void RapidRaycast::on_actionSave_activated()
{
	QString qFilename = mapFilename;

	// Ask where to save to, incase map filename was new
	if (0 == mapFilename.length())
	{
		qFilename = QFileDialog::getSaveFileName(this, "Save Map File", "", "All map files (*.rmp)");
	}


	// Valid filename?
	if (qFilename.length())
	{
		SaveWorld(qFilename);
	}
}

void RapidRaycast::on_actionSaveAs_activated()
{
	// Ask where to save to
	QString qFilename = QFileDialog::getSaveFileName(this, "Save Map File", "", "All map files (*.rmp)");


	// Valid filename?
	if (qFilename.length())
	{
		SaveWorld(qFilename);
	}
}


void RapidRaycast::on_actionToolResize_activated()
{
	on_actionResize_activated();
}

void RapidRaycast::on_actionResize_activated()
{
	resizeDialog->show();
}


void RapidRaycast::slotResize(unsigned int width, unsigned int height)
{
	world->Resize(width, height);
}

void RapidRaycast::slotChanged()
{
	UpdateStates();
}

void RapidRaycast::UpdateStates()
{
	// If raycasting, basically, disable allmost all buttons
	if (raycast)
	{
		ui.actionResize->setEnabled(false);
		ui.actionToolResize->setEnabled(false);
		ui.actionNew->setEnabled(false);
		ui.actionLoad->setEnabled(false);
		ui.actionSave->setEnabled(false);
		ui.actionSaveAs->setEnabled(false);
		ui.actionChange_default_texture->setEnabled(false);
		ui.actionChange_default_floor->setEnabled(false);
		ui.actionChange_default_ceiling->setEnabled(false);
	}
	else
	{
		ui.actionResize->setEnabled(true);
		ui.actionToolResize->setEnabled(true);
		ui.actionNew->setEnabled(true);
		ui.actionLoad->setEnabled(true);
		ui.actionSaveAs->setEnabled(true);
		ui.actionChange_default_texture->setEnabled(true);
		ui.actionChange_default_floor->setEnabled(true);
		ui.actionChange_default_ceiling->setEnabled(true);
		ui.actionSave->setEnabled(true);
	}
}

void RapidRaycast::on_actionChange_default_texture_activated()
{
	world->slotChangeDefaultTexture(DefaultTextureWidget::DEFAULT);
}

void RapidRaycast::on_actionChange_default_floor_activated()
{
	world->slotChangeDefaultTexture(DefaultTextureWidget::FLOOR);
}

void RapidRaycast::on_actionChange_default_ceiling_activated()
{
	world->slotChangeDefaultTexture(DefaultTextureWidget::CEILING);
}

void RapidRaycast::InitializeHelp()
{
	// Create new help browser
	help = new HelpBrowser(this);
}

void RapidRaycast::on_actionHelpContents_activated()
{
	help->ShowPage("index.html");
	help->show();
}

void RapidRaycast::on_actionTutorial_activated()
{
	help->ShowPage("tutorial.html");
	help->show();
}

void RapidRaycast::on_actionAbout_activated()
{
	QMessageBox::about(this, "About",
		"RapidRaycast 1.0\n\n" \
		"RapidRaycast is an application for creating and modeling top-down 2D maps, which can be visualized" \
		" in 2.5D using a raycasting technique.\n\n" \
		"Authors:\n" \
		"\tDomien Nowicki\n" \
		"\tBjorn Schobben\n" \
		"");
}

void RapidRaycast::on_actionAboutQt_activated()
{
	QApplication::aboutQt();
}

void RapidRaycast::on_actionOpenGLRenderer_activated()
{
	ui.actionSoftwareRenderer->setChecked(false);
	ui.actionOpenGLRenderer->setChecked(true);
	
	// Real-time renderer switch
	if (raycast)
	{
		raycast->SetRenderer(RaycasterWidget::OPENGL);
	}
}

void RapidRaycast::on_actionSoftwareRenderer_activated()
{
	ui.actionOpenGLRenderer->setChecked(false);
	ui.actionSoftwareRenderer->setChecked(true);

	// Real-time renderer switch
	if (raycast)
	{
		raycast->SetRenderer(RaycasterWidget::SOFTWARE);
	}
}

